package edu.purdue.cs.ds;

import java.math.BigInteger;
import java.net.Socket;
import java.security.SecureRandom;
import java.util.ArrayList;

import edu.purdue.cs.ds.vss.*;

/**
 * The Dealer distributes shares of the secret to
 * the servers in the host list.
 * @author jwallrab
 *
 */
public class Dealer {

	NetworkCommunicator net;
	ArrayList<Socket> serverSockets;
	ArrayList<String> servers;
	String filename = "files/hosts";
	String secretString = "";
	VerifiableSharing secret;
	ServerSets sets;
	ArrayList<ArrayList<BigInteger>> serverShares;
	IndexSets iset;
	int k;
	int n;
	int l;
	
	/**
	 * Constructor
	 * @param p The port to connect on
	 * @param s The Secret to be Split
	 * @param k The Number of Shares to Recover the Secret
	 */
	public Dealer(int p, String s, int kval){
		
		k = kval;
		net = new NetworkCommunicator(p);
		serverSockets = new ArrayList<Socket>();
		servers = FileIO.readFile(filename);
		n = servers.size();
		secretString = s;
		iset = new IndexSets();
		sets = new ServerSets(n, k);
		serverShares = new ArrayList<ArrayList<BigInteger>>();
		for(int i = 0; i < n; ++i)
			serverShares.add(new ArrayList<BigInteger>());
		l = sets.getL();
		
		createShares();
		sendShares();
	}
	
	public void createShares(){
		SecureRandom sr = new SecureRandom();
		BigInteger r =  new BigInteger(Constants.q.bitLength() - 1, sr);
		BigInteger s =  new BigInteger(Constants.q.bitLength() - 1, sr);

		System.out.println("Main secret " + s);
		System.out.println("Random " + r);
		
		Generator g = new Generator(s, r , l);
		
		secret = g.generateVerifiableSharing();
	}
	
	/**
	 * Send the shares to the servers
	 */
	public void sendShares(){
		
		ArrayList<ArrayList<VerifiableShare>> shares = constructServerShares();
		for(int i = 0; i < servers.size(); ++i){
			try{
				serverSockets.add(net.sendConnect(servers.get(i)));
				net.sendInt(shares.get(i).size());
				for(int j = 0; j < shares.get(i).size(); ++j)
					net.sendObject(shares.get(i).get(j));
			}catch(Exception e){
				System.out.println("Error: Send failure to host " + servers.get(i));
			}
		}
	}
	
	/**
	 * Give Server p the proper subset of the L shares
	 * @param set
	 * @return
	 */
	public ArrayList<ArrayList<VerifiableShare>> constructServerShares(){
		
		ArrayList<ArrayList<Integer>> servSets = sets.constructServerSets();
		
		ArrayList<BigInteger> shares = secret.getS();
		
		ArrayList<Share> shareList = new ArrayList<Share>();
		ParentIndex parentIndex = new ParentIndex(0, 0, null);
		for(int i = 0; i < shares.size(); i ++) {
			Share tmpShare = new Share(shares.get(i),i,parentIndex);
			tmpShare.setA(secret.getA());
			tmpShare.setR(secret.getR().get(i));
			shareList.add(tmpShare);
		}
		
		//Depth 1
		IndexSets is = new IndexSets();
		ArrayList<ArrayList<Share>> serverShares = is.generateShareSets2(n, shareList, servSets);
		
		
		ArrayList<ArrayList<VerifiableShare>> vs = new ArrayList<ArrayList<VerifiableShare>>();
		for(int i = 0; i < serverShares.size(); ++i){
			vs.add(new ArrayList<VerifiableShare>());
			
			for(int j = 0; j < serverShares.get(i).size(); ++j){
				
				Share thisShare = serverShares.get(i).get(j);
				
				VerifiableShare tmp = new VerifiableShare(
						thisShare.getR(),thisShare.getA(),
						secret.getA().get(0),thisShare,null);
				
					System.out.println("Gave Share " + serverShares.get(i).get(j).getIndex() + " to server " + i);
					vs.get(i).add(tmp);
			}
		}
		return vs;
		
		/*
		
		ArrayList<ArrayList<VerifiableShare>> shares = new ArrayList<ArrayList<VerifiableShare>>();
		ArrayList<ArrayList<Integer>> indexSet = iset.generateIndexSets(n, set);
		
		for(int i = 0; i < indexSet.size(); ++i){
			shares.add(new ArrayList<VerifiableShare>());
			for(int j = 0; j < indexSet.get(i).size(); ++j){
				
				Share tmpshare = new Share(secret.getS().get(indexSet.get(i).get(j)),
											indexSet.get(i).get(j),new ParentIndex(0,0,null));
				tmpshare.setR(secret.getR().get(indexSet.get(i).get(j)));
				tmpshare.setA(secret.getA());
				tmpshare.setLambda(null);
				
				VerifiableShare tmp = new VerifiableShare(
						secret.getR().get(indexSet.get(i).get(j)),secret.getA(),
						secret.getA().get(0),tmpshare,null);
				
					System.out.println("Gave Share " + indexSet.get(i).get(j) + " to server " + i);
					shares.get(i).add(tmp);
			}
		}
		return shares;*/
	}
}
